---@class ActorInfo
---@field ActorAddr integer 对象指针
---@field ActorData integer 对象数据指针
---@field ActorType integer 对象类型
---@field Location Vector3 对象坐标
---@field Distance number 距离
---@field InteractID InteractID64 交互ID

---@class ItemInfo : ActorInfo

---@class GizmoInfo : ActorInfo

local M = {}

M.localPlayer = nil
M.localActorID = 0
M.CurrentHealthPotion = 0
M.MaxHealthPotion = 0

function M.CustomHandle()
    M.localPlayer = Game.GetLocalPlayer()
    if not M.localPlayer then
        return
    end
    M.localActorID = Game.GetLocalActorID()
    M.CurrentHealthPotion = Game.GetActorAttributeData(M.localPlayer, Game.AttributeID["CurrentHealthPotion"]) or 0
    M.MaxHealthPotion = Game.GetActorAttributeData(M.localPlayer, Game.AttributeID["MaxHealthPotion"]) or 0
    --print("当前血瓶:" .. M.CurrentHealthPotion .. " 最大血瓶:" .. M.MaxHealthPotion)
    M.HandleSelf()
    if Game.GetKeyState(0x20) ~= 0 then
        M.HandleTarget()
    end
end

---处理自身
function M.HandleSelf()
    if not M.localPlayer then return end
    local HP = Game.GetActorHP(M.localPlayer)
    if not HP then return end
    local MaxHP = Game.GetActorMaxHP(M.localPlayer)
    if not MaxHP then return end
    if HP / MaxHP < 0.5 then              --血量低于50%自动喝血瓶
        if M.CurrentHealthPotion > 0 then --检查是否有血瓶
            Game.KeyClick(81)
        end
    end
end

---处理索敌
function M.HandleTarget()
    local RoleName = Game.GetRoleName()
    local Monsters, Items, Gizmos = M.GetActorArray()
    Tool.TriggerInterval(1, 0.5, function()
        M.HandleGizmo(Gizmos)
        M.HandleItem(Items)
    end)
    if not D4.skill.data[RoleName] then
        print("未找到职业:" .. RoleName .. " 的技能数据,请检查!")
    end
    local target = M.GetTarget(Monsters)
    if target then
        local SkillList = Game.GetSkillSlotList()
        if not SkillList then return end
        if #SkillList == 0 then return end
        local SkillArray = {}
        for _, SkillID in ipairs(SkillList) do
            local SkillLevel = Game.GetSkillLevel(SkillID)
            if SkillLevel and SkillLevel > 0 then
                if D4.skill.data[RoleName][SkillID] then
                    table.insert(SkillArray, { ID = SkillID, Info = D4.skill.data[RoleName][SkillID] })
                end
            end
        end
        if #SkillArray > 0 then
            table.sort(SkillArray, function(a, b) return (a.Info.priority or 0) > (b.Info.priority or 0) end)
            for _, data in ipairs(SkillArray) do
                local state = data.Info.condition(
                    { ID = data.ID, Location = target.Location, Distance = target.Distance, Array = Monsters })
                if state == 1 then
                    Game.UseSkill(data.ID, target.InteractID, target.Location, true)
                elseif state == 0 then
                    Game.UseSkill(data.ID, -1, Tool.ToVector3(0, 0, 0), true)
                end
            end
        end
        local MouseLocation = Game.GetMouseLocation()
        Game.MoveToLocation(MouseLocation)
    else
        local MouseLocation = Game.GetMouseLocation()
        Game.MoveToLocation(MouseLocation)
    end
end

---处理物品
function M.HandleItem(Items)
    if not Items then return end
    if #Items == 0 then return end
    for _, item in ipairs(Items) do
        if item and item.InteractID then
            local Name = Game.GetItemName(item.ActorAddr)
            if Name then
                if Name:find("Health_Potion") then
                    if M.CurrentHealthPotion < M.MaxHealthPotion then
                        Game.Interaction(item.InteractID)
                    end
                else
                    Game.Interaction(item.InteractID)
                end
            end
        end
    end
end

---处理物体
---@param Gizmos GizmoInfo[] 物体数组
function M.HandleGizmo(Gizmos)
    if not Gizmos then return end
    if #Gizmos == 0 then return end
    for _, gizmo in ipairs(Gizmos) do
        if gizmo and gizmo.InteractID then
            local GizmoType = Game.GetGizmoType(gizmo.ActorData)
            if GizmoType then
                if GizmoType == D4.enum.GizmoType.Door                                --门
                    or GizmoType == D4.enum.GizmoType.Chest                           --宝箱
                    or GizmoType == D4.enum.GizmoType.Shrine                          --神龛
                    or GizmoType == D4.enum.GizmoType.Event_Reward_Chest              --事件奖励箱
                    or GizmoType == D4.enum.GizmoType.Breakable_Container             --可破坏容器
                    or GizmoType == D4.enum.GizmoType.Traversal                       --穿越点
                    or GizmoType == D4.enum.GizmoType.Switch                          --开关
                    or GizmoType == D4.enum.GizmoType.Breakable_Container_Arrangement --可破坏容器组合
                    or GizmoType == D4.enum.GizmoType.Quest_Chest                     --任务宝箱
                    or GizmoType == D4.enum.GizmoType.Unique_Operator_Chest           --特殊操作宝箱
                    or GizmoType == D4.enum.GizmoType.Participant_Timer               --参与者计时器
                then
                    Game.Interaction(gizmo.InteractID)
                end
            end
        end
    end
end

---获取当前目标
---@param Monsters ActorInfo[] 怪物数组
---@return ActorInfo?
function M.GetTarget(Monsters)
    if not Monsters then return end
    if #Monsters == 0 then return end
    local target = Monsters[1]
    if not target then return end
    local ActorAddr = target.ActorAddr
    local ActorData = target.ActorData
    local ActorType = target.ActorType
    local Location = target.Location
    local Distance = target.Distance
    local InteractID = target.InteractID
    if not ActorAddr or not ActorData or not ActorType or not Location or not Distance or not InteractID then return end

    return target
end

---获取物体数组
---@return ActorInfo[]
---@return ItemInfo[]
---@return GizmoInfo[]
function M.GetActorArray()
    local Monsters = {}
    local Items = {}
    local Gizmos = {}

    if not M.localPlayer then return {}, {}, {} end
    if not M.localActorID then return {}, {}, {} end
    local MeLocation = Game.GetActorLocation(M.localPlayer)
    if not MeLocation then return {}, {}, {} end

    local list = Game.GetActorList()
    if not list then return {}, {}, {} end
    for _, addr in ipairs(list) do
        if not addr then goto continue end
        local Data = Game.GetActorData(addr)
        if not Data then goto continue end
        local Type = Game.GetActorDataType(Data)
        if not Type then goto continue end
        if Type == D4.enum.ActorType.Monster then
            local ChildType = Game.GetActorChildType(addr) >> 24
            if ChildType ~= 19 then goto continue end
            local HP = Game.GetActorHP(addr)
            if HP and HP > 0 then
                local Location = Game.GetActorLocation(addr)
                table.insert(Monsters, {
                    ActorAddr = addr,
                    ActorData = Data,
                    ActorType = Type,
                    ChildType = ChildType,
                    Location = Location,
                    Distance = Game.GetDistance(MeLocation, Location),
                    InteractID = Game.GetActorInteractID64(addr)
                })
            end
        elseif Type == D4.enum.ActorType.Item then
            local Location = Game.GetActorLocation(addr)
            local Distance = Game.GetDistance(MeLocation, Location)
            if Distance > 10 then goto continue end
            local BeLongActorID = Game.GetActorBeLongActorID(addr)
            if not BeLongActorID then goto continue end
            --local InventoryEmplacement = Game.GetActorInventoryEmplacement(addr)
            if BeLongActorID == M.localActorID then goto continue end
            table.insert(Items, {
                ActorAddr = addr,
                ActorData = Data,
                ActorType = Type,
                Location = Location,
                Distance = Distance,
                InteractID = Game.GetActorInteractID64(addr)
            })
        elseif Type == D4.enum.ActorType.Gizmo then
            local Location = Game.GetActorLocation(addr)
            local Distance = Game.GetDistance(MeLocation, Location)
            if Distance > 10 then goto continue end
            table.insert(Gizmos, {
                ActorAddr = addr,
                ActorData = Data,
                ActorType = Type,
                Location = Location,
                Distance = Distance,
                InteractID = Game.GetActorInteractID64(addr)
            })
        end
        ::continue::
    end
    table.sort(Gizmos, function(a, b) return a.Distance < b.Distance end)
    table.sort(Items, function(a, b) return a.Distance < b.Distance end)
    table.sort(Monsters, function(a, b) return a.Distance < b.Distance end)

    return Monsters, Items, Gizmos
end

return M
